﻿#include "FITKOCCTransformation.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKGeoEnum.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKVirtualTopoManager.h"
#include "FITKOCCVirtualTopoCreator.h"

#include "FITK_Kernel/FITKAppFramework/FITKAppFramework.h"
#include "FITK_Kernel/FITKAppFramework/FITKGlobalData.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKGeoCommandList.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKGeoInterfaceFactory.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKAbsGeoModelSurface.h"

#include <TopoDS.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Edge.hxx>
#include <gp_Trsf.hxx>
#include <gp_GTrsf.hxx>
#include <BRepBuilderAPI_Transform.hxx>
#include <gp_Ax1.hxx>
#include <TopLoc_Location.hxx>
#include <Precision.hxx>
#include <BRepBuilderAPI_GTransform.hxx>
namespace OCC {
    // 构造函数
    FITKOCCModelTransform::FITKOCCModelTransform() :OCCShapeAgent(this)
    {
        _shapeAgent = _occShapeAgent;
    }

    // 更新数据
    bool FITKOCCModelTransform::update()
    {
        if (m_SourceShape.isNull()) return false;

        auto geoCmdList = FITKGLODATA->getGeometryData<Interface::FITKGeoCommandList>();

        auto cmd = geoCmdList->getDataByID(m_SourceShape.CmdId);
        if (cmd == nullptr) return false;

        auto agent = dynamic_cast<OCC::FITKAbstractOCCModel*>(cmd->getShapeAgent());
        if (agent == nullptr) return false;
        TopoDS_Shape* shape = agent->getShape();
        if (shape->IsNull()) return false;

        try {
            gp_Trsf trans;
            trans.SetTranslation(gp_Vec(m_Vector[0], m_Vector[1], m_Vector[2]));

            auto newShape = shape->Located(shape->Location() * trans);
            _occShapeAgent->updateShape(newShape);
        }
        catch (...)
        {
            printLog(tr("Failed to transform!"), 3);
            return false;
        }
        return true;
    }

    // 构造函数
    FITKOCCModelTransformByTwoPoints::FITKOCCModelTransformByTwoPoints() :OCCShapeAgent(this)
    {
        _shapeAgent = _occShapeAgent;
    }

    // 更新数据
    bool FITKOCCModelTransformByTwoPoints::update()
    {
        if (m_SourceShape.isNull()) return false;

        auto geoCmdList = FITKGLODATA->getGeometryData<Interface::FITKGeoCommandList>();

        auto cmd = geoCmdList->getDataByID(m_SourceShape.CmdId);
        if (cmd == nullptr) return false;

        auto agent = dynamic_cast<OCC::FITKAbstractOCCModel*>(cmd->getShapeAgent());
        if (agent == nullptr) return false;
        TopoDS_Shape* shape = agent->getShape();
        if (shape->IsNull()) return false;

        m_Vector[0] = m_EndPoint[0] - m_StartPoint[0];
        m_Vector[1] = m_EndPoint[1] - m_StartPoint[1];
        m_Vector[2] = m_EndPoint[2] - m_StartPoint[2];

        try {
            gp_Trsf trans;
            trans.SetTranslation(gp_Vec(m_Vector[0], m_Vector[1], m_Vector[2]));

            auto newShape = shape->Located(shape->Location() * trans);
            _occShapeAgent->updateShape(newShape);
        }
        catch (...)
        {
            printLog(tr("Failed to transform!"), 3);
            return false;
        }
        return true;
    }

    // 构造函数
    FITKOCCModelTransformByDirAndDis::FITKOCCModelTransformByDirAndDis() :OCCShapeAgent(this)
    {
        _shapeAgent = _occShapeAgent;
    }

    // 更新数据
    bool FITKOCCModelTransformByDirAndDis::update()
    {
        if (m_SourceShape.isNull()) return false;

        auto geoCmdList = FITKGLODATA->getGeometryData<Interface::FITKGeoCommandList>();

        auto cmd = geoCmdList->getDataByID(m_SourceShape.CmdId);
        if (cmd == nullptr) return false;

        auto agent = dynamic_cast<OCC::FITKAbstractOCCModel*>(cmd->getShapeAgent());
        if (agent == nullptr) return false;
        TopoDS_Shape* shape = agent->getShape();
        if (shape->IsNull()) return false;

        gp_Vec vec(m_Direction[0], m_Direction[1], m_Direction[2]);
        auto mag = vec.Magnitude();
        if (mag <= Precision::Confusion()) {
            printLog(tr("The direction cannot be zero vector!"), 3);
            return false;
        }
        vec = m_Distance / mag * vec;
        m_Vector = { vec.X(), vec.Y(), vec.Z() };

        try {
            gp_Trsf trans;
            trans.SetTranslation(vec);

            auto newShape = shape->Located(shape->Location() * trans);
            _occShapeAgent->updateShape(newShape);
        }
        catch (...)
        {
            printLog(tr("Failed to transform!"), 3);
            return false;
        }
        return true;
    }

    // 构造函数
    FITKOCCModelRotate::FITKOCCModelRotate() :OCCShapeAgent(this)
    {
        _shapeAgent = _occShapeAgent;
    }

    // 更新数据
    bool FITKOCCModelRotate::update()
    {
        if (m_SourceShape.isNull()) return false;

        auto geoCmdList = FITKGLODATA->getGeometryData<Interface::FITKGeoCommandList>();

        auto cmd = geoCmdList->getDataByID(m_SourceShape.CmdId);
        if (cmd == nullptr) return false;
        auto agent = dynamic_cast<OCC::FITKAbstractOCCModel*>(cmd->getShapeAgent());
        if (agent == nullptr) return false;
        TopoDS_Shape* shape = agent->getShape();
        if (shape->IsNull()) return false;

        try {
            gp_Trsf trans;
            gp_Ax1 rotateAxis(
                gp_Pnt(m_AxisStartPoint[0], m_AxisStartPoint[1], m_AxisStartPoint[2]),
                gp_Dir(m_AxisEndPoint[0] - m_AxisStartPoint[0], m_AxisEndPoint[1] - m_AxisStartPoint[1], m_AxisEndPoint[2] - m_AxisStartPoint[2])
            );
            trans.SetRotation(rotateAxis, m_Angle);

            auto newShape = shape->Located(shape->Location() * trans);
            _occShapeAgent->updateShape(newShape);

        }
        catch (...)
        {
            printLog(tr("Failed to transform!"), 3);
            return false;
        }
        return true;
    }

    // 构造函数
    FITKOCCModelScale::FITKOCCModelScale() :OCCShapeAgent(this)
    {
        _shapeAgent = _occShapeAgent;
    }

    // 更新数据
    bool FITKOCCModelScale::update()
    {
        if (m_SourceShape.isNull()) return false;

        auto geoCmdList = FITKGLODATA->getGeometryData<Interface::FITKGeoCommandList>();

        auto cmd = geoCmdList->getDataByID(m_SourceShape.CmdId);
        if (cmd == nullptr) return false;
        auto agent = dynamic_cast<OCC::FITKAbstractOCCModel*>(cmd->getShapeAgent());
        if (agent == nullptr) return false;
        TopoDS_Shape* shape = agent->getShape();
        if (shape->IsNull()) return false;

        try {
            // 如果基准点不在原点的话需要进行两次平移操作
            bool isOriginBase = (std::abs(m_BasePoint[0]) <= Precision::Confusion()) &&
                (std::abs(m_BasePoint[1]) <= Precision::Confusion()) &&
                (std::abs(m_BasePoint[2]) <= Precision::Confusion());

            TopoDS_Shape newShape = *shape;

            // 平移基准点到原点
            if (!isOriginBase) {
                gp_Trsf trsf;
                trsf.SetTranslation(gp_Vec(-m_BasePoint[0], -m_BasePoint[1], -m_BasePoint[2]));
                newShape = newShape.Located(newShape.Location() * trsf);
            }

            // 进行缩放
            gp_GTrsf gtrsf;
            gtrsf.SetValue(1, 1, m_Factors[0]);
            gtrsf.SetValue(2, 2, m_Factors[1]);
            gtrsf.SetValue(3, 3, m_Factors[2]);
            BRepBuilderAPI_GTransform transform(newShape, gtrsf);
            newShape = transform.Shape();

            // 从原点移动到基准点
            if (!isOriginBase) {
                gp_Trsf trsf;
                trsf.SetTranslation(gp_Vec(m_BasePoint[0], m_BasePoint[1], m_BasePoint[2]));
                newShape = newShape.Located(newShape.Location() * trsf);
            }

            //auto newShape = shape->Located(shape->Location() * trans);
            _occShapeAgent->updateShape(newShape);

        }
        catch (...)
        {
            printLog(tr("Failed to scale!"), 3);
            return false;
        }
        return true;
    }

}


